﻿!function ($) {
    $.widget('codecontrib.collapsibleTable', {
        _collapseRow: function ($currentRow, $toggleLink) {
            var $plugin = this;

            var currentRowLevel = $plugin._getRowLevel($currentRow);

            $currentRow.addClass('collapsed')
				.removeClass('expanded')
				.nextAll()
				.each(function () {
				    var $row = $(this);
				    var rowLevel = $plugin._getRowLevel($row);

				    if (!rowLevel
						|| rowLevel <= currentRowLevel) {
				        // We're looping through a lower level; stop collapsing.
				        return false;
				    }

				    if ($row.css('display') === 'none') {
				        return;
				    }

				    $row.css('display', 'none')
						.data('RowLevel', currentRowLevel);
				});
        },
        _collapseRows: function () {
            var $plugin = this;

            $(String.format('#css-collapsed-{0}', $plugin._tableIndex)).remove();

            if (!$(String.format('#css-collapsed-{0}', $plugin._tableIndex)).length) {
                $('head').append($plugin._cssCollapsed);
            }

            $plugin.element.find('tbody > tr.level-1.expanded')
				.removeClass('expanded');

            $plugin.element.find('tbody > tr')
				.filter(function () {
				    var $row = $(this);
				    var rowLevel = $plugin._getRowLevel($row);
				    var rowDisplay = $row.css('display');

				    return (rowLevel > 1
						&& (rowDisplay === 'block' || rowDisplay === 'table-row'));
				})
				.each(function () {
				    var $row = $(this);

				    $row.css('display', 'none')
						.removeClass('collapsed')
						.removeClass('expanded')
						.removeData('RowLevel');
				});
        },
        _create: function () {
            var $plugin = this;

            if (!$plugin.element.is('table')) {
                return;
            }

            $plugin.element.addClass('collapsible');

            $plugin._tableIndex = $('table.collapsible').length;

            var tableId = $plugin.element.attr('id');

            if (!tableId) {
                tableId = String.format('collapsible-{0}', $plugin._tableIndex);

                $plugin.element.attr('id', tableId);
            }

            $plugin._browserRowDisplay = ($.browser.msie) ? 'block' : 'table-row';
            $plugin._cssExpanded = $(String.format('<style id="css-expanded-{0}" type="text/css">#{1} tbody > tr { display: {2}; } #{1} tbody > tr.level-1 { display: {2}; } #{1} tbody > tr:not(.collapsed) a.toggle-row { background-image: url({3}); }</style>', $plugin._tableIndex, tableId, $plugin._browserRowDisplay, $plugin.options.collapseIconUrl));
            $plugin._cssCollapsed = $(String.format('<style id="css-collapsed-{0}" type="text/css">#{1} tbody > tr { display: none; } #{1} tbody > tr.level-1 { display: {2}; } #{1} tbody > tr:not(.expanded) a.toggle-row { background-image: url({3}); }</style>', $plugin._tableIndex, tableId, $plugin._browserRowDisplay, $plugin.options.expandIconUrl));

            $plugin.element.find('tbody > tr')
				.filter(function () {
				    var classNames = $(this).attr('className');

				    return (classNames
						&& classNames.match(/level-/));
				})
				.each(function () {
				    var $currentRow = $(this);
				    var currentRowLevel = $plugin._getRowLevel($currentRow);
				    var nextRowLevel = $plugin._getRowLevel($currentRow.next());

				    var $cell = $currentRow.children('td:first');

				    var paddingLeft = parseInt($cell.css('padding-left')) + ((currentRowLevel - 1) * $plugin.options.paddingOffset);

				    var $textContainer = $("<span/>");
				    $textContainer.html($cell.html());

				    $cell.empty();

				    if (nextRowLevel
						&& currentRowLevel < nextRowLevel) {
				        var $toggleLink = $plugin._createToggleLink();

				        $cell.append($toggleLink);
				    }
				    else {
				        $currentRow.addClass('leaf');

				        paddingLeft += $plugin.options.paddingOffset;
				    }

				    $cell.append($textContainer)
                        .css('padding-left', paddingLeft + 'px');
				});

            if ($plugin.options.collapseAll) {
                $plugin._collapseRows();
            }

            // Collapse all and expand all links. 
            if ($plugin.element.find($plugin._getLevelSelector(2)).length) {
                var $expandAllLink = $('<a/>');
                $expandAllLink.addClass('expand-rows')
					.attr('href', '#')
					.click(function (event) {
					    $plugin._expandRows();

					    return false;
					});

                var $collapseAllLink = $('<a/>');
                $collapseAllLink.addClass('collapse-rows')
					.attr('href', '#')
					.click(function (event) {
					    $plugin._collapseRows();

					    return false;
					});

                var $container = $('<div/>');
                $container.addClass('toggle-rows-container')
					.append($expandAllLink)
					.append($collapseAllLink);

                $container.insertBefore($plugin.element);
            }
        },
        _createToggleLink: function () {
            var $plugin = this;

            var $toggleLink = $('<a/>');
            $toggleLink.addClass('toggle-row')
				.attr('href', '#')
				.click(function (event) {
				    var $currentRow = $toggleLink.parents('tr:first');
				    var currentRowLevel = $plugin._getRowLevel($currentRow);
				    var $childRow = $plugin._getChildRow($currentRow, currentRowLevel);

				    if ($childRow.css('display') === 'none') {
				        $plugin._expandRow($currentRow, $toggleLink);
				    }
				    else {
				        $plugin._collapseRow($currentRow, $toggleLink);
				    }

				    return false;
				})
				.maxZIndex();

            return $toggleLink;
        },
        _expandRow: function ($currentRow, $toggleLink) {
            var $plugin = this;

            var currentRowLevel = $plugin._getRowLevel($currentRow);

            $currentRow.addClass('expanded')
				.removeClass('collapsed')
				.nextAll()
				.each(function () {
				    var $row = $(this);
				    var rowLevel = $plugin._getRowLevel($row);
				    var collapsedAtRowLevel = $row.data('RowLevel');

				    if (!rowLevel
						|| rowLevel <= currentRowLevel) {
				        // Stop expanding, we're looping through a lower level.
				        return false;
				    }

				    if (rowLevel > (currentRowLevel + 1)
						&& ((!collapsedAtRowLevel || collapsedAtRowLevel !== currentRowLevel) || $plugin._getParentRow($row, rowLevel).hasClass('collapsed'))) {
				        // Don't expand child nodes that have been previously collapsed; continue looping.
				        return;
				    }

				    $row.css('display', $plugin._browserRowDisplay)
						.removeData('RowLevel');
				});
        },
        _expandRows: function () {
            var $plugin = this;

            $(String.format('#css-expanded-{0}', $plugin._tableIndex)).remove();

            if (!$(String.format('#css-expanded', $plugin._tableIndex)).length) {
                $('head').append($plugin._cssExpanded);
            }

            $plugin.element.find('tbody > tr.level-1.collapsed')
				.removeClass('collapsed');

            $plugin.element.find('tbody > tr')
				.filter(function () {
				    var $row = $(this);
				    var rowLevel = $plugin._getRowLevel($row);
				    var rowDisplay = $row.css('display');

				    return (rowLevel > 1
						&& rowDisplay === 'none');
				})
				.each(function () {
				    var $row = $(this);

				    $row.css('display', $plugin._browserRowDisplay)
						.removeClass('collapsed')
						.removeClass('expanded');
				});
        },
        _getChildRow: function ($row, level) {
            return $row.nextAll('.level-' + (level + 1) + ':first');
        },
        _getLevelClassName: function (level) {
            var $plugin = this;

            return 'level-' + level;
        },
        _getLevelSelector: function (level) {
            var $plugin = this;

            return '.' + $plugin._getLevelClassName(level);
        },
        _getParentRow: function ($row, level) {
            var $plugin = this;

            return $row.prevAll($plugin._getLevelSelector(level - 1) + ':first');
        },
        _getRowLevel: function ($row) {
            var classNames = $row.attr('class');

            if (classNames != null) {
                var levelPrefix = 'level-';

                classNames = classNames.split(' ');

                for (var i = 0; i < classNames.length; i++) {
                    var className = classNames[i];

                    if (className.indexOf(levelPrefix) === 0) {
                        return parseInt(className.replace(levelPrefix, ''));
                    }
                }
            }

            return null;
        },
        options: {
            collapseAll: true,
            collapseIconUrl: '',
            expandIconUrl: '',
            paddingOffset: 15
        }
    })
} (window.jQuery)